package com.piz.dao.common;

import java.io.Serializable;
import java.util.Collection;
import java.util.Date;
import java.util.List;

import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Expression;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

/**
 * Generic DAO implementation
 * 
 * @author vschauhan
 * 
 */
public abstract class PizHiberImpl<T> extends HibernateDaoSupport implements
		PizHiber<T> {
	
	/**
	 * Inserts a row in the table
	 * 
	 * @param obj
	 */
	public void insert(T obj) {
		getHibernateTemplate().save(obj);
	}

	/**
	 * Deletes row from the table based on primary key
	 * 
	 * @param obj
	 */
	public void delete(T obj) {
		getHibernateTemplate().delete(obj);
	}

	/**
	 * Updates row in the table based on primary key
	 * 
	 * @param obj
	 */
	public void update(T obj) {	
		getHibernateTemplate().update(obj);
	}

	/**
	 * Gets row from the table when primary key is specified.
	 * 
	 * @return the persistent instance, or null if not found
	 */
	@SuppressWarnings("unchecked")
	public T selectBasedOnPrimaryKey(Class<T> cls, T obj) {
		obj = getHibernateTemplate().get(cls, (Serializable) obj);
		return obj;
	}

	/**
	 * Returns list, when value of the fields based on whom selection is to be
	 * done, are specified. </br> values can not be part of composite primary
	 * key.</br> Rest fields are to be essentially kept null.
	 * 
	 * @param obj
	 * @return a List containing 0 or more persistent instances
	 */
	@SuppressWarnings("unchecked")
	public List<T> select(final T obj) {
		List<T> result = getHibernateTemplate().findByExample(obj);
		return result;
	}

	/**
	 * Saves if not exists ; or Updates if exists.save
	 * 
	 * @param obj
	 */
	public void saveOrUpdate(T obj) {
		getHibernateTemplate().saveOrUpdate(obj);
	}

	/**
	 * Saves if not exists ; or Updates if exists.save
	 * 
	 * @param Collection
	 *            <T> col
	 */
	public void saveOrUpdateAll(Collection<T> col) {
		getHibernateTemplate().saveOrUpdateAll(col);
	}

	/**
	 * Selects all rows from a table.
	 * 
	 * @param tablename
	 */
	@SuppressWarnings("unchecked")
	public List<T> selectAll(String tablename) {
		StringBuffer buffer = new StringBuffer();
		buffer.append("from ");
		buffer.append(tablename);
		buffer.append(" ");
		buffer.append(tablename.toLowerCase());
		List<T> result = getHibernateTemplate().find(buffer.toString());
		return result;
	}

	/**
	 * Executes native sql query to select; only one column is specified in
	 * native SQL. Returns a list of objects.
	 * 
	 * @param query
	 * @return list
	 */
	@SuppressWarnings("unchecked")
	public List<Object> executeNativeSQLSingleColumn(final String query) {
		return (List<Object>) this.getHibernateTemplate().execute(
				new HibernateCallback() {
					public Object doInHibernate(Session session)
							throws HibernateException {
						SQLQuery query1 = session.createSQLQuery(query);
						return query1.list();
					}
				});
	}

	/**
	 * Executes native sql query to select; multiple columns are specified in
	 * native SQL. Returns a list of object array.
	 * 
	 * @param query
	 * @return list
	 */
	@SuppressWarnings("unchecked")
	public List<Object[]> executeNativeSQLMultipleColumns(final String query) {
		return (List<Object[]>) this.getHibernateTemplate().execute(
				new HibernateCallback() {
					public Object doInHibernate(Session session)
							throws HibernateException {
						SQLQuery query1 = session.createSQLQuery(query);
						return query1.list();
					}
				});
	}

	/**
	 * Executes update, insert or delete SQL queries.
	 * 
	 * @param query
	 */
	public Integer executeNativeSQLUpdate(final String query) {
		return (Integer) (this.getHibernateTemplate()
				.execute(new HibernateCallback() {
					public Object doInHibernate(Session session)
							throws HibernateException {
						SQLQuery query1 = session.createSQLQuery(query);
						return query1.executeUpdate();
					}
				}));
	}

	/**
	 * Executes HQL query to update the table.
	 * 
	 * @param hqlQuery
	 */
	public void executeHQLUpdate(final String hqlQuery) {
		this.getHibernateTemplate().execute(new HibernateCallback() {
			public Object doInHibernate(Session session)
					throws HibernateException {
				Query query1 = session.createQuery(hqlQuery);
				return query1.executeUpdate();
			}
		});
	}

	/**
	 * Executes HQL query to select data.
	 * 
	 * @param hqlQuery
	 */
	@SuppressWarnings("unchecked")
	public List<T> executeHQL(final String hqlQuery) {
		List<T> result = this.getHibernateTemplate().find(hqlQuery);
		return result;
	}

	/**
	 * Hibernate Criteria API
	 */
	public List<T> findByCriteria(DetachedCriteria criteria) {
		return getHibernateTemplate().findByCriteria(criteria);
	}
	
	public List<T> findByDateRangeCriteria(Class<T> cls,
			String dateRangeColumn, Date startDate, Date endDate) {

		DetachedCriteria crit = DetachedCriteria.forClass(cls);
		crit.add(Expression.between(dateRangeColumn,
				new Date(startDate.getTime()), new Date(endDate.getTime())));
		return findByCriteria(crit);
	}

	public List<T> selectByDateRangeCriteria(String query, Date date) {

		List<T> result = null;
		result = this.getHibernateTemplate().find(query, date);
		return result;
	}

	@Override
	public T search(Class<T> cls, long Id) {
		return this.getHibernateTemplate().get(cls, Id);
	}
	/**
	 * this method will return a criteria
	 */
	@Override
	public DetachedCriteria getCriteria(Class<T> entityBean){
		DetachedCriteria crit = DetachedCriteria.forClass(entityBean);
		return crit;
	}
}
